package edu.mse.camel.client.whiteboard.gef.ui.commands;

import java.util.Iterator;

import org.eclipse.gef.commands.Command;

import edu.mse.camel.client.whiteboard.gef.ui.WhiteBoardUtil;
import edu.mse.camel.client.whiteboard.gef.ui.editor.WhiteboardModelManager;
import edu.mse.camel.client.whiteboard.gef.whiteboard.Connection;
import edu.mse.camel.client.whiteboard.gef.whiteboard.Shape;



/**
 * A command to create a connection between two shapes.
 * The command can be undone or redone.
 * <p>
 * This command is designed to be used together with a GraphicalNodeEditPolicy.
 * To use this command properly, following steps are necessary:
 * </p>
 * <ol>
 * <li>Create a subclass of GraphicalNodeEditPolicy.</li>
 * <li>Override the <tt>getConnectionCreateCommand(...)</tt> method, 
 * to create a new instance of this class and put it into the CreateConnectionRequest.</li>
 * <li>Override the <tt>getConnectionCompleteCommand(...)</tt>  method,
 * to obtain the Command from the ConnectionRequest, call setTarget(...) to set the
 * target endpoint of the connection and return this command instance.</li>
 * </ol>
 * @see org.eclipse.gef.examples.shapes.parts.ShapeEditPart#createEditPolicies() for an
 * 			 example of the above procedure.
 * @see org.eclipse.gef.editpolicies.GraphicalNodeEditPolicy
 * @author Elias Volanakis
 * @author Chris Aniszczyk - updated for EMF example
 */
public class ConnectionCreateCommand extends AbstractWhiteboardCommand {
	/** The connection instance. */
	private Connection connection;
	/** The desired line style for the connection (dashed or solid). */
	private final boolean lineStyle;
	
	/** Start endpoint for the connection. */
	private final Shape source;
	/** Target endpoint for the connection. */
	private Shape target;
	
	/**
	 *	Instantiate a command that can create a connection between two shapes.
	 * @param source the source endpoint (a non-null Shape instance)
	 * @param lineStyle the desired line style. See Connection#setLineStyle(int) for details
	 * @throws IllegalArgumentException if source is null
	 * @see Connection#setLineStyle(int)
	 */
	public ConnectionCreateCommand(Shape source, boolean lineStyle) {
		if (source == null) {
			throw new IllegalArgumentException();
		}
		setLabel("connection creation");
		this.source = source;
		this.lineStyle = lineStyle;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#canExecute()
	 */
	public boolean canExecute() {
		// disallow source -> source connections
		if (source.equals(target)) {
			return false;
		}
		// return false, if the source -> target connection exists already
		for (Iterator iter = source.getSourceConnections().iterator(); iter.hasNext();) {
			Connection conn = (Connection) iter.next();
			if (conn.getTarget().equals(target)) {
				return false;
			}
		}
		return true;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#execute()
	 */
	public void execute() {
		// create a new connection between source and target
		connection = WhiteboardModelManager.getFactory().createConnection();
		connection.setSource(source);
		connection.setTarget(target);
		connection.setDashed(lineStyle);
		connection.setColor(WhiteBoardUtil.getMyColor());
		source.getSourceConnections().add(connection);
		target.getTargetConnections().add(connection);
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#redo()
	 */
	public void redo() {
		source.getSourceConnections().add(connection);
		target.getTargetConnections().add(connection);
	}
	
	/**
	 * Set the target endpoint for the connection.
	 * @param target that target endpoint (a non-null Shape instance)
	 * @throws IllegalArgumentException if target is null
	 */
	public void setTarget(Shape target) {
		if (target == null) {
			throw new IllegalArgumentException();
		}
		this.target = target;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#undo()
	 */
	public void undo() {
		source.getSourceConnections().remove(connection);
		target.getTargetConnections().remove(connection);
	}
}
